package com.gitee.kenewstar.migration.function;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.gitee.kenewstar.migration.constants.Const;
import com.gitee.kenewstar.migration.constants.FunctionCode;
import com.gitee.kenewstar.migration.core.AbstractSyncFunction;
import com.gitee.kenewstar.migration.core.ContextParam;
import com.gitee.kenewstar.migration.entity.CommonResult;
import com.gitee.kenewstar.migration.entity.Menu;
import com.gitee.kenewstar.migration.entity.MenuPermission;
import com.gitee.kenewstar.migration.entity.MenuTl;
import com.gitee.kenewstar.migration.service.MenuService;
import lombok.Builder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author kenewstar
 */
@Slf4j
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class MenuFunction extends AbstractSyncFunction<Menu> {

    private final MenuService menuService;

    public static final String FD_LEVEL = "fd_level";
    public static final String ORG = "organization";
    public static final String SITE = "site";

    private String getFdLevel() {
        String level = Optional.ofNullable(getParameter(FD_LEVEL)).orElse(ORG);
        Assert.isTrue(Objects.equals(ORG, level) || Objects.equals(SITE, level), "fd_level只能为 organization 或 site");
        return level;
    }

    @Override
    protected Class<Menu> parameterType() {
        return Menu.class;
    }

    @Override
    public List<CommonResult> execute(ContextParam contextParam, List<Menu> list) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }

        final List<Menu> menuList = this.menuService.devSelectMenuList(list, getFdLevel());
        if (CollUtil.isEmpty(menuList)) {
            return Collections.emptyList();
        }
        final List<Long> menuIds = menuList.stream().map(Menu::getId).collect(Collectors.toList());
        final List<MenuTl> menuTls = this.menuService.devSelectMenuTl(menuIds);
        final List<MenuPermission> menuPermissions = this.menuService.devSelectMenuPermission(menuIds);

        // 获取父菜单数据
        final List<Long> parentIds = menuList.stream().filter(Menu::getRootFlag).map(Menu::getParentId).distinct().collect(Collectors.toList());
        final List<Menu> parentMenus = this.menuService.devSelectMenuByIds(parentIds);
        // dev -> uat id
        final Map<Long, Long> menuMap = this.menuService.queryMenuMap(parentMenus);


        final Map<Long, List<MenuTl>> menuTlsMap = menuTls.stream().collect(Collectors.groupingBy(MenuTl::getId));
        final Map<Long, List<MenuPermission>> menuPermissionsMap = menuPermissions.stream().collect(Collectors.groupingBy(MenuPermission::getMenuId));

        contextParam.menuContext = Context.builder().build();
        contextParam.menuContext.menuTlsMap = menuTlsMap;
        contextParam.menuContext.menuPermissionsMap = menuPermissionsMap;
        contextParam.menuContext.idParentMap = new HashMap<>(16);

        List<CommonResult> resultList = new ArrayList<>(menuList.size());
        for (Menu menu : menuList) {
            CommonResult result = CommonResult.create(true).setTenantId(menu.getHTenantId())
                    .setMenuCode(menu.getCode()).setFdLevel(getFdLevel());
            try {
                if (menu.getRootFlag()) {
                    menu.setParentId(menuMap.get(menu.getParentId()));
                }
                contextParam.menuContext.menu = menu;
                this.self().singleExecute(contextParam);
            } catch (Exception e) {
                result.setSuccessFlag(false).setErrorMsg(e.getMessage());
                e.printStackTrace();
            } finally {
                resultList.add(result);
            }
        }

        return resultList;
    }

    @Override
    public FunctionCode functionCode() {
        return FunctionCode.MENU;
    }

    @Override
    @DS(Const.UAT)
    @Transactional(rollbackFor = Exception.class)
    public void singleExecute(ContextParam contextParam) {
        Menu menu = contextParam.menuContext.menu;
        Map<Long, List<MenuTl>> menuTlsMap = contextParam.menuContext.menuTlsMap;
        Map<Long, List<MenuPermission>> menuPermissionsMap = contextParam.menuContext.menuPermissionsMap;
        Map<Long, Long> idParentMap = contextParam.menuContext.idParentMap;

        final Long devId = menu.getId();
        menu.setId(null);
        if (Boolean.FALSE.equals(menu.getRootFlag())) {
            menu.setParentId(Optional.ofNullable(idParentMap.get(menu.getParentId())).orElse(0L));
        }
        this.menuService.uatInsertMenu(ListUtil.toList(menu));
        idParentMap.put(devId, menu.getId());

        final List<MenuTl> menuTls = menuTlsMap.get(devId);
        if (CollUtil.isNotEmpty(menuTls)) {
            menuTls.forEach(x -> x.setId(menu.getId()));
            this.menuService.uatInsertMenuTl(menuTls);
        }

        final List<MenuPermission> menuPermissions = menuPermissionsMap.get(devId);
        if (CollUtil.isNotEmpty(menuPermissions)) {
            menuPermissions.forEach(x -> x.setMenuId(menu.getId()));
            menuPermissions.forEach(x -> x.setId(null));
            this.menuService.uatInsertMenuPermission(menuPermissions);
        }

    }

    @Builder
    @SuppressWarnings("all")
    public static class Context {
        public Menu menu;
        public Map<Long, List<MenuTl>> menuTlsMap;
        public Map<Long, List<MenuPermission>> menuPermissionsMap;
        // id -> parentId
        public Map<Long, Long> idParentMap;

    }

}
